import math
import numpy
import pyglet
from pyglet.gl import *
from awesum import *
from car import Car
from track_parts import *

class SceneTrack(Scene):
	MAX_CAM_DIST = 20
	MIN_CAM_DIST = 10
	
	def __init__(self, track, players=(Player.Type.LOCAL,)):
		super(SceneTrack, self).__init__()
		
		self._debugMode = False	#Show spheres for cam points, make everything wireframe
		self._feelerColor = 1.		#DEBUG, for changing the feeler sphere's red
		self.spawn = None #Temporarily a single Vector until respawn point lists are in level files
		self.grid = None
		self.prevTpHit = None #Last track piece the car was over
		self.trackFile = track
		self.car = []
		
		for playerType in players:
			self.car.append(Car(playerType))
		
		self.drawables.extend(self.car)
	def load(self, game=None):
		self.grid = TrackGrid(game, 24, 19, 23)
		level = game.loadLevel(self.trackFile if self.trackFile else "wr.track")
		if "block" in level:
			block = level["block"]
			block[2] = -block[2]
			game["BLOCK_SIZE"] = Vector3(block)
		for car in level["car"]:
			pos = car["pos"]
			self.car[0].origin = self.spawn = self.grid.getCoords(pos[0], pos[1], pos[2])
			if len(self.car) > 1: #DEBUG - change to use multiple spawn points
				self.car[1].origin = self.car[0].origin.copy()
				self.car[1].origin.x += 2.
			break
		for part in level["part"]:
			pos = part["pos"]
			size = part["size"]
			coeff = part["coeff"] if "coeff" in part else 1
			form = part["form"] if "form" in part else 0
			if   part["type"] == "v":		tp = TrackVertical(Vector3(pos), Vector3(size), coeff, form)
			elif part["type"] == "h":		tp = TrackHorizontal(Vector3(pos), Vector3(size), coeff, form)
			elif part["type"] == "ctl":	tp = TrackTLCurve(Vector3(pos), Vector3(size))
			elif part["type"] == "ctr":	tp = TrackTRCurve(Vector3(pos), Vector3(size))
			elif part["type"] == "cbl":	tp = TrackBLCurve(Vector3(pos), Vector3(size))
			elif part["type"] == "cbr":	tp = TrackBRCurve(Vector3(pos), Vector3(size))
			elif part["type"] == "ast":	tp = SurfaceTop(Vector3(pos), Vector3(size))
			elif part["type"] == "asb":	tp = SurfaceBottom(Vector3(pos), Vector3(size))
			elif part["type"] == "asf":	tp = SurfaceFront(Vector3(pos), Vector3(size))
			elif part["type"] == "asbk":	tp = SurfaceBack(Vector3(pos), Vector3(size))
			elif part["type"] == "asl":	tp = SurfaceLeft(Vector3(pos), Vector3(size))
			elif part["type"] == "asr":	tp = SurfaceRight(Vector3(pos), Vector3(size))
			elif part["type"] == "aetf":	tp = EdgeTF(Vector3(pos), Vector3(size))
			elif part["type"] == "aetb":	tp = EdgeTB(Vector3(pos), Vector3(size))
			elif part["type"] == "aetl":	tp = EdgeTL(Vector3(pos), Vector3(size))
			elif part["type"] == "aetr":	tp = EdgeTR(Vector3(pos), Vector3(size))
			elif part["type"] == "aebf":	tp = EdgeBF(Vector3(pos), Vector3(size))
			elif part["type"] == "aebb":	tp = EdgeBB(Vector3(pos), Vector3(size))
			elif part["type"] == "aebl":	tp = EdgeBL(Vector3(pos), Vector3(size))
			elif part["type"] == "aebr":	tp = EdgeBR(Vector3(pos), Vector3(size))
			elif part["type"] == "aelf":	tp = EdgeLF(Vector3(pos), Vector3(size))
			elif part["type"] == "aelb":	tp = EdgeLB(Vector3(pos), Vector3(size))
			elif part["type"] == "aerf":	tp = EdgeRF(Vector3(pos), Vector3(size))
			elif part["type"] == "aerb":	tp = EdgeRB(Vector3(pos), Vector3(size))
			elif part["type"] == "actlf":	tp = CornerTLF(Vector3(pos), Vector3(size))
			elif part["type"] == "actlb":	tp = CornerTLB(Vector3(pos), Vector3(size))
			elif part["type"] == "actrf":	tp = CornerTRF(Vector3(pos), Vector3(size))
			elif part["type"] == "actrb":	tp = CornerTRB(Vector3(pos), Vector3(size))
			elif part["type"] == "acblf":	tp = CornerBLF(Vector3(pos), Vector3(size))
			elif part["type"] == "acblb":	tp = CornerBLB(Vector3(pos), Vector3(size))
			elif part["type"] == "acbrf":	tp = CornerBRF(Vector3(pos), Vector3(size))
			elif part["type"] == "acbrb":	tp = CornerBRB(Vector3(pos), Vector3(size))
			self._addTrackPiece(tp)
		
		super(SceneTrack, self).load(game)
		self.grid.buildGrid()
		self.car[0].curTrackPart = self.grid.getTrackAt(self.grid.blockFromCoords(self.car[0].origin))
		if len(self.car) > 1: #DEBUG
			self.car[1].curTrackPart = self.grid.getTrackAt(self.grid.blockFromCoords(self.car[1].origin))
			self.car[1].move.quat *= Quaternion().setEuler(Vector3(0., 45., 0.))
		self.textures.loadTex("road.bmp", Texture.Type.SQUARE)
		self.game.cameras[0].set(self._getPosBehindCar(0), self._getPosPastCar(0), Vector3(0, 1, 0), True)
		if len(self.car) > 1: #DEBUG
			self.game.cameras.append(Camera())
			self.game.cameras[1].set(self._getPosPastCar(1), self._getPosBehindCar(1), Vector3(0, 1, 0), True)
	def draw(self):
		super(SceneTrack, self).draw()
		if self.game.debugMode:
			behind = self._getPosBehindCar()
			past = self._getPosPastCar()
			feeler = self._getTPFeelerPos(self.car)
			q = gluNewQuadric()
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) #wireframe
			gluQuadricNormals(q, GLU_SMOOTH)
			gluQuadricTexture(q, GL_TRUE)
			#Blue ball behind car
			glPushMatrix()
			glColor3f(0., 0., .5)
			glTranslatef(behind.x, behind.y, behind.z)
			gluSphere(q, .5, 10, 10)
			glPopMatrix()
			#Yellow ball in front of car
			glPushMatrix()
			glColor3f(.5, .5, 0.)
			glTranslatef(past.x, past.y, past.z)
			gluSphere(q, .5, 10, 10)
			glPopMatrix()
			#R ball in front of car
			glPushMatrix()
			glColor3f(self._feelerColor, 0., 0.)
			glTranslatef(feeler.x, feeler.y, feeler.z)
			gluSphere(q, .5, 10, 10)
			glPopMatrix()
	def step(self):
		if Key.Z in self.game.keys:
			self.respawn(0)
		
		for i in range(len(self.car)):
			#Delay the camera location
			self.game.cameras[i].moveTo(self._getPosBehindCar(i), SceneTrack.MAX_CAM_DIST)
			#Don't delay the camera focus position
			self.game.cameras[i].view = self._getPosPastCar(i)
			#Set the camera's PYR to the same plane as the car
			self.game.cameras[i].up = self._getCarNormal(i)
		
		#Move everything goin across track parts
		self._stepMovers()
		
		super(SceneTrack, self).step()
	
	def respawn(self, i):
		"""Reset the car to starting position and orientation"""
		self.car[i].origin = self.spawn
		self.car[i].move.quat = Quaternion().setEulerDeg(Vector3(0., 180., 0.))
		self.car[i].move.speed = 0.
	
	def _getPosBehindCar(self, i):
		"""Return pos above and behind the car to where the camera should move"""
		v = self.car[i].move.quat.rotateV(Vector3(0., 4., -SceneTrack.MIN_CAM_DIST))
		return v.add(self.car[i].origin)
	def _getPosPastCar(self, i):
		"""Return pos above and in front of the car where the camera should look"""
		v = self.car[i].move.quat.rotateV(Vector3(0., 3., 5.))
		return v.add(self.car[i].origin)
	def _getCarNormal(self, i):
		"""Return a vector representing the car's normal unit vector"""
		return self.car[i].move.quat.rotateV(Vector3(0., 1., 0.))
	def _getTPFeelerPos(self, d):
		"""Point in front of the car to find the correct next track piece"""
		v = d.move.quat.rotateV(Vector3(0., 1., 2.))
		v *= max(d.move.speed, 1.)
		return v.add(d.origin)
	
	def _addTrackPiece(self, tp):
		self.drawables.append(tp)
		return tp
	
	def _stepMovers(self):
		for mover in self.drawables:
			if mover.move:
				#Find current track piece for feeler
				above = self._getTPFeelerPos(mover)
				block = self.grid.blockFromCoords(above)
				tp = self.grid.getTrackAt(block)
				
				#if it has changed since last step, and the car isn't too far from the new find...
				th = mover.move.speed		#threshold for being slightly outside the TP
				if tp and tp != mover.curTrackPart:
					ox, oy, oz = mover.origin
					if ((
						ox >= tp.xMin - th and ox <= tp.xMax + th) and (
						oy >= tp.yMin - th and oy <= tp.yMax + th) and (
						oz >= tp.zMin - th and oz <= tp.zMax + th)) and (
						not (isinstance(tp, RaceTrackPart) and abs(oy - (block.y * self.game["BLOCK_SIZE"].y)) > 1.)):
						#update stored tp
						tp.firstStep = True
						mover.curTrackPart = tp
					else:
						pass
						#self._feelerColor = 1.
						#print ox >= tp.xMin - th and ox <= tp.xMax + th, oy >= tp.yMin - th and oy <= tp.yMax + th, oz >= tp.zMin - th and oz <= tp.zMax + th, not (isinstance(tp, RaceTrackPart) and abs(oy - (carBlock.y * TrackPart.BLOCK_HEIGHT)) > 1.)
				
				mover.origin = mover.curTrackPart.getNextPoint(mover)

class TrackGrid(object):
	def __init__(self, game, x=0, y=0, z=0):
		self.game = game
		self._grid = numpy.zeros((x, y, z), dtype="object")
	
	def buildGrid(self):
		#Add track exits to the grid for detection while driving
		for t in self.game.scene.drawables:
			if isinstance(t, TrackPart):
				for v in t.getExits():
					otherTP = self.getTrackAt(v)
					#if not otherTP:
					self.add(v, t)
					#elif tp != otherTP: #single-block TPs get a duplicate, so ignore it
					#	print "Track overlap at: ", v, "when adding", tp, "other:", otherTP
	
	def setDims(self, v):
		self._grid = numpy.zeros((v.x, v.y, v.z), dtype="object")
		
	def add(self, v, obj):
		self._grid[v.x, v.y, v.z] = obj
	
	def getTrackAt(self, v):
		return self._grid[v.x, v.y, v.z]
	
	def getTrackAtCoords(self, v):
		return self.getTrackAt(self.blockFromCoords(v))
	
	def getCoords(self, x, y, z):
		block = self.game["BLOCK_SIZE"]
		return Vector3(x * block.x, y * block.y, z * block.z)
		
	def getCoordsV(self, v):
		block = self.game["BLOCK_SIZE"]
		return Vector3(v.x * block.x, v.y * block.y, v.z * block.z)
	
	def blockFromCoords(self, v):
		block = self.game["BLOCK_SIZE"]
		return Vector3(int(v.x / block.x), int(v.y / block.y), int(v.z / block.z))
